package com.allwees.bs.module.oms.service.impl;

import cn.hutool.core.lang.UUID;
import com.alibaba.fastjson.JSONArray;
import com.allwees.bs.core.model.constant.CacheName;
import com.allwees.bs.core.model.order.wf.order.EOrderAction;
import com.allwees.bs.core.model.order.wf.order.EOrderItemAction;
import com.allwees.bs.core.model.order.wf.order.EOrderItemState;
import com.allwees.bs.core.model.order.wf.refund.ERefundAction;
import com.allwees.bs.core.model.order.wf.refund.ERefundState;
import com.allwees.bs.core.model.payment.enums.EPaymentType;
import com.allwees.bs.module.lms.entity.LogisticsWaybill;
import com.allwees.bs.module.lms.vo.TrackDetailsDto;
import com.allwees.bs.module.oms.account.runner.AccountTransitUtils;
import com.allwees.bs.module.oms.context.processor.OrderRefundPostProcessor;
import com.allwees.bs.module.oms.dao.*;
import com.allwees.bs.module.oms.entity.*;
import com.allwees.bs.module.oms.req.PlatFormCancelShippedReq;
import com.allwees.bs.module.oms.service.GeneralOmsOrderItemService;
import com.allwees.bs.module.oms.vo.TrackingDetailVo;
import com.allwees.bs.module.oms.wf.EOmsOrderAction;
import com.allwees.core.cache.RedisUtil;
import com.allwees.core.common.constant.EStatus;
import com.allwees.core.common.i18n.I18nMessage;
import com.allwees.core.common.i18n.MessageSourceUtil;
import com.allwees.core.common.util.DateUtil;
import com.allwees.support.payment.model.RefundInfo;
import com.allwees.support.payment.oceanpay.config.OceanpayProperties;
import com.allwees.support.payment.payby.PaybyAccountStrategy;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@Service
public class GeneralOmsOrderItemServiceImpl extends BaseOmsOrderItemServiceImpl implements GeneralOmsOrderItemService {

    @Autowired
    private OrderRefundPostProcessor orderRefundPostProcessor;

    @Autowired
    private AccountTransitUtils accountTransitUtils;


    @Resource
    private OrderItemDao orderItemDao;

    @Resource
    private OrderDao orderDao;

    @Resource
    protected OrderRefundDao orderRefundDao;

    @Resource
    protected OrderRefundItemDao orderRefundItemDao;

    @Resource
    private PaymentLogDao paymentLogDao;

    @Resource
    private OmsOrderItemDao omsOrderItemDao;

    @Autowired
    private OceanpayProperties properties;

    @Autowired
    private I18nMessage i18nMessage;

    @Override
    @Transactional
    public void doSettle(Set<String> orderItemUuids) {
        Date now = new Date();
        orderItemUuids.forEach(orderItemUuid -> {
            OmsOrderItem omsOrderItem = new OmsOrderItem();
            omsOrderItem.setUuid(orderItemUuid);
            omsOrderItem.setJoinSettleAt(now);
            omsOrderItem.setEStatus(EStatus.OMS_TO_SETTLE);
            updateByUuid(omsOrderItem);
        });
    }

    @Override
    public void doSettled(OmsOrderItem omsOrderItem, boolean exceedSettleDate) {
        Date now = new Date();
        omsOrderItem.setSettledAt(now);
        omsOrderItem.setSettleState(EStatus.OMS_SETTLED.value());
        if (exceedSettleDate) {//30天后自动结算，订单状态变成已收货
            omsOrderItem.setRefunded(false);
            omsOrderItem.setEStatus(EStatus.OMS_RECEIVED);
            OrderItem orderItem = orderItemDao.findByUuid(omsOrderItem.getUuid());
            orderItem.setRefundAble(false);//30天后自动结算,不可以退货
            orderItem.setState(EOrderItemState.RECEIVED);
            orderItemDao.updateByUuid(orderItem, omsOrderItem.getUuid());

        }

        //omsOrderItem.setEStatus(EStatus.OMS_SETTLED);
        updateByUuid(omsOrderItem);

    }

    @Override
    @Transactional(rollbackFor = Throwable.class)
    public OmsOrderItem doSettled(OmsOrderItem omsOrderItem, String branchUuid, boolean exceedSettleDate) {
        //OmsOrderItem item = findToSettleItems(omsOrderItem.getUuid(),branchUuid);
        doSettled(omsOrderItem, exceedSettleDate);
        return omsOrderItem;
    }

    @Override
    public List<OmsOrderItem> findByOrderUuid(String orderUuid) {
        return baseMapper.findByOrderUuid(orderUuid);
    }

    @Override
    @Transactional
    public void doApplyRefund(String orderItemUuid) {
        orderRefundPostProcessor.doApplyRefund(orderItemUuid, null, null);
    }

    @Override
    @Transactional
    public void doRefundedByRetailer(String orderItemUuid) {
        OmsOrderItem omsOrderItem = checkByUuid(orderItemUuid);

        EStatus status = EStatus.OMS_REFUND;

        omsOrderItem.setRefundAt(new Date());
        omsOrderItem.setRefunded(true);


        omsOrderItem.setEStatus(status);
        omsOrderItem.setSettleState(null);
        //log.info("==================doRefunded==================,omsOrderItem:{}",omsOrderItem);
        updateByUuid(omsOrderItem);
        //omsOrderItemDa o.clearSettleStateByUuid(orderItemUuid);

        //log.info("==================doRefunded==================,a:{}",a);
        _doRefundedByRetailer(orderItemUuid);
        OmsOrder omsOrder = omsOrderDao.findByUuid(omsOrderItem.getOrderUuid());
        omsOrder.setRefundCount(omsOrder.getRefundCount() + omsOrderItem.getQuantity());
        omsOrderDao.updateByUuid(omsOrder, omsOrderItem.getOrderUuid());
    }

    @Override
    @Transactional
    public void doRefundedByPlatform(OmsOrderItem omsOrderItem) {
        PlatFormCancelShippedReq platFormCancelShippedReq = new PlatFormCancelShippedReq();
        platFormCancelShippedReq.setRemarks(MessageSourceUtil.getMessage("otherStatus.TIMEOUT_SHIP"));
        platFormCancelShippedReq.setReason(21);
        platFormCancelShippedReq.setType(0);

        OrderItem orderItem = orderItemDao.findByUuid(omsOrderItem.getUuid());
        if (null == orderItem) {
            return;
        }

        Order order = orderDao.findByUuid(omsOrderItem.getOrderUuid());
        if (null == order) {
            return;
        }
        if (EPaymentType.COD.equals(order.getPaymentType())) {
            return;
        }
        OrderRefund orderRefund = new OrderRefund(platFormCancelShippedReq, order, orderItem);
        orderRefund.setState(ERefundState.START);

        ERefundAction refundAction = ERefundAction.PLATFORM_CANCEL_SHIPPED;
        orderRefund.signal(refundAction);
        orderRefund.setEStatus(EStatus.OMS_REFUND);


        orderRefundDao.insert(orderRefund);
        OrderRefundItem orderRefundItem = new OrderRefundItem(orderItem);
        orderRefundItem.setOrderRefundUuid(orderRefund.getUuid());
        orderRefundItemDao.insert(orderRefundItem);

        EStatus status = EStatus.OMS_REFUND;

        omsOrderItem.setRefundAt(new Date());
        omsOrderItem.setRefunded(true);

        omsOrderItem.setEStatus(status);
        omsOrderItem.setSettleState(null);
        //log.info("==================doRefunded==================,omsOrderItem:{}",omsOrderItem);
        updateByUuid(omsOrderItem);
        //omsOrderItemDao.clearSettleStateByUuid(orderItemUuid);
        //log.info("==================doRefunded==================,a:{}",a);
        _doRefundedByPlatform(omsOrderItem.getUuid());
        OmsOrder omsOrder = omsOrderDao.findByUuid(omsOrderItem.getOrderUuid());
        omsOrder.setRefundCount(omsOrder.getRefundCount() + omsOrderItem.getQuantity());
        omsOrderDao.updateByUuid(omsOrder, omsOrderItem.getOrderUuid());
    }

    @Override
    @Transactional
    public void doRefunded(String orderItemUuid, boolean isApproved) {
        OmsOrderItem omsOrderItem = checkByUuid(orderItemUuid);
        Assert.isTrue(EStatus.OMS_REFUNDING.equals(omsOrderItem.getEStatus()), "当前状态不可以退货");

        EStatus status = EStatus.OMS_RECEIVED;
        if (isApproved) {
            omsOrderItem.setRefundAt(new Date());
            omsOrderItem.setRefunded(true);
            status = EStatus.OMS_REFUND;
        }
        omsOrderItem.setEStatus(status);
        omsOrderItem.setSettleState(null);
        //log.info("==================doRefunded==================,omsOrderItem:{}",omsOrderItem);
        updateByUuid(omsOrderItem);
        omsOrderItemDao.clearSettleStateByUuid(orderItemUuid);

        //log.info("==================doRefunded==================,a:{}",a);
        _doRefunded(orderItemUuid, isApproved);
        OmsOrder omsOrder = omsOrderDao.findByUuid(omsOrderItem.getOrderUuid());
        omsOrder.setRefundCount(omsOrder.getRefundCount() + omsOrderItem.getQuantity());
        omsOrderDao.updateByUuid(omsOrder, omsOrderItem.getOrderUuid());
//        OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);
//        if (isApproved) {
//            Customer user = customerDao.findByUuid(orderItem.getUserUuid());
//            refundLogActionUtil.logAction(orderItem, EActionType.SKU_SHIPPED_REFUND, user.getIsInfluencer());
//        }
    }

    @Override
    public void doReviewed(String orderItemUuid) {
        //OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);

        OmsOrderItem omsOrderItem = new OmsOrderItem();
        omsOrderItem.setUuid(orderItemUuid);
        omsOrderItem.setReviewAt(DateUtil.now());
        omsOrderItem.setEStatus(EStatus.COMPLETED);
        updateByUuid(omsOrderItem);
    }

    @Override
    public void doCanceled(String orderItemUuid) {
        OmsOrderItem omsOrderItem = new OmsOrderItem();
        omsOrderItem.setUuid(orderItemUuid);
        omsOrderItem.setCanceled(true);//setReviewAt();
        omsOrderItem.setUpdatedAt(DateUtil.now());
        omsOrderItem.setEStatus(EStatus.OMS_CANCELED);
        updateByUuid(omsOrderItem);
        OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);
        String orderUuid = orderItem.getOrderUuid();
        List<OrderItem> orderItemList = orderItemDao.findByOrderUuid(orderUuid);
        List<OrderItem> canceledList = orderItemList.stream().filter(o -> EOrderItemState.CANCELED.name().equals(o.getState().name())).collect(Collectors.toList());
        log.info("OrderItem:canceledList size:{}", canceledList.size());
        int cancelNum = canceledList.size();
        Order order = orderDao.findByUuid(orderUuid);
        if (order.getQuantity() == cancelNum) {
            OmsOrder omsOrder = omsOrderDao.findByUuid(orderUuid);
            order.signal(EOrderAction.USER_ALL_CANCELED);
            //更新 order
            orderDao.updateByUuid(order, orderUuid);
            EOmsOrderAction action = EOmsOrderAction.USER_ALL_CANCELED;
            omsOrder.signal(action);
            //更新oms order
            omsOrderDao.updateByUuid(omsOrder, orderUuid);
            log.info("USER_ALL_CANCELED:orderItemUuid :{}", orderItemUuid);
        }
    }

    private void _doRefunded(String orderItemUuid, boolean isApproved) {
        OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);
        EOrderItemAction action = EOrderItemAction.PLATFORM_REJECT_REFUND;
        if (isApproved) {
            action = EOrderItemAction.PLATFORM_CONFIRM_REFUND;
            orderItem.setRefundAt(new Date());
            orderItem.setRefunded(true);
            //退还Payby
            _refundAmt(orderItemUuid);
            // 按sku子订单项粒度退AmtCash
            orderRefundPostProcessor.doOrderItemCashRefunded(orderItem.getUuid());
            accountTransitUtils.accountTransit(orderItem, "doRefundAmtShipped");
            refundProductLimitNum(orderItem);
        }
        orderItem.signal(action);
        orderItemDao.updateByUuid(orderItem, orderItemUuid);

    }

    //退款时归还商品限购数量
    private void refundProductLimitNum(OrderItem orderItem) {
        Integer numPerUser = RedisUtil.hget(CacheName.PROD_PRODUCT_MODEL_BY_UUID, orderItem.getProductUuid());
        if (numPerUser == null || numPerUser == 0) {
            return;
        }
        Integer buyedNum = RedisUtil.hget(CacheName.USER_BUY_PRODUCT_NUM, orderItem.getUserUuid() + ":" + orderItem.getProductUuid());
        log.info("======refundProductLimitNum=======userUuid:{},has buyed num:{}, productUuid:{}", orderItem.getUserUuid(), buyedNum, orderItem.getProductUuid());
        if (buyedNum == null) {
            buyedNum = 0;
        }
        buyedNum = buyedNum - orderItem.getQuantity();
        RedisUtil.hset(CacheName.USER_BUY_PRODUCT_NUM, orderItem.getUserUuid() + ":" + orderItem.getProductUuid(), buyedNum);
    }

    private void _doRefundedByPlatform(String orderItemUuid) {
        OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);
        EOrderItemAction action = EOrderItemAction.PLATFORM_CANCEL_SHIPPED;
        orderItem.setRefundAt(new Date());
        orderItem.setRefunded(true);
        //退还Payby
        try {
            _refundAmt(orderItemUuid);
        } catch (Exception e) {
            log.info("====_doRefundedByPlatform_refundAmt ,orderItemUuid:{},===== e：{}, ", orderItemUuid, e);
        }
        // 按sku子订单项粒度退AmtCash
        orderRefundPostProcessor.doOrderItemCashRefunded(orderItem.getUuid());
        accountTransitUtils.accountTransit(orderItem, "doPlatformRefundAmt");
        refundProductLimitNum(orderItem);
//        Customer user =  customerDao.findByUuid(orderItem.getUserUuid());
//        refundLogActionUtil.logAction(orderItem,EActionType.SKU_UNSHIPPED_REFUND,user.getIsInfluencer());
        try {
            orderItem.signal(action);
        } catch (Exception e) {
            log.info("====_doRefundedByPlatform_signal===== orderItemUuid:{},e：{}", orderItemUuid, e);
        }
        orderItemDao.updateByUuid(orderItem, orderItemUuid);

    }

    private void _doRefundedByRetailer(String orderItemUuid) {
        OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);
        EOrderItemAction action = EOrderItemAction.RETAILER_CANCEL_SHIPPED_ON_LIMIT;
        if (orderItem.getCanShipping()) {
            action = EOrderItemAction.RETAILER_CANCEL_SHIPPED_AFTER_LIMIT;
        }

        orderItem.setRefundAt(new Date());
        orderItem.setRefunded(true);
        //退还PayPal
        _refundAmt(orderItemUuid);
        // 按sku子订单项粒度退AmtCash
        orderRefundPostProcessor.doOrderItemCashRefunded(orderItem.getUuid());
        accountTransitUtils.accountTransit(orderItem, "doRetailerRefundAmt");
        refundProductLimitNum(orderItem);
//        Customer user = customerDao.findByUuid(orderItem.getUserUuid());
//        refundLogActionUtil.logAction(orderItem,EActionType.SKU_UNSHIPPED_REFUND,user.getIsInfluencer());
        orderItem.signal(action);
        orderItemDao.updateByUuid(orderItem, orderItemUuid);
    }

    public void _refundAmt(String orderItemUuid) {
        OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);
        //todo add by zhouyw Mock和订单金额为0时直接返回
        if (orderItem.getPlatform().isMock() || BigDecimal.ZERO.compareTo(orderItem.getAmt()) == 0) {
            return;
        }
        PaymentLog paymentLog = paymentLogDao.findByOrderUuid(orderItem.getOrderUuid());

        OrderRefundItem orderRefundItem = orderRefundItemDao.findByOrderItemUuid(orderItemUuid);

        // get refundNo
        String refundNo;
        if (orderRefundItem != null) {
            refundNo = orderRefundItem.getUuid();
        } else {
            refundNo = UUID.randomUUID().toString();
        }

        RefundInfo refundInfo = new RefundInfo()
                .setOrderItemUuid(orderItemUuid)
                .setPaymentNo(paymentLog.getPaymentNumber())
                .setOrderNo(orderItem.getOrderUuid())
                .setAmt(orderItem.getAmt())
                .setCurrency(paymentLog.getCurrency())
                .setRefundNo(refundNo);

        PaybyAccountStrategy.refund(refundInfo);

//        String type = paymentLog.getType();
//        if (EPayMethod.PAYPAL.name().equals(type)) {
//            PaypalAccountStrategy.refund(refundInfo);
//        } else if(EPayMethod.CREDIT_CARD.name().equals(type)) {
//            CreditCardStrategy.refund(refundInfo);
//        }
//        }
    }

    @Override
    public TrackingDetailVo queryTrackingDetail(String orderItemUuid) {
        LogisticsWaybill logisticsWaybill = logisticsWaybillDao.findOneByField("order_item_uuid", orderItemUuid);
        if (logisticsWaybill == null) {
            return null;
        }

        TrackingDetailVo trackingDetailDTO = new TrackingDetailVo();
        trackingDetailDTO.setCode(logisticsWaybill.getStatus());
        trackingDetailDTO.setDelivered(logisticsWaybill.getDelivered());
        trackingDetailDTO.setDeliverDate(logisticsWaybill.getDeliverDate());
        trackingDetailDTO.setTrackingNumber(logisticsWaybill.getExpressCode());
        if (logisticsWaybill.getTrackingDetail() != null) {
            trackingDetailDTO.setData(JSONArray.parseObject(logisticsWaybill.getTrackingDetail(), JSONArray.class));
        }
        return trackingDetailDTO;
    }

    /**
     * 妥投
     */
    public void doDelivered(String orderItemUuid, TrackDetailsDto tracking) {
        OrderItem orderItem = orderItemDao.findByUuid(orderItemUuid);
        orderItem.setDeliverAt(tracking.getDeliverDate());
        EOrderItemAction action = EOrderItemAction.LOGISTICS_DELIVERED;
        orderItem.signal(action);
        if (null == orderItem.getPaymentAt()) {
            orderItem.setPaymentAt(orderItem.getDeliverAt());
        }
        orderItemDao.updateByUuid(orderItem, orderItemUuid);

        OmsOrderItem omsOrderItem = findByUuid(orderItemUuid);
        omsOrderItem.setSettleState(EStatus.OMS_TO_SETTLE.value());
        omsOrderItem.setJoinSettleAt(tracking.getDeliverDate());
        omsOrderItem.setEStatus(EStatus.OMS_DELIVERED);
        omsOrderItem.setDeliverAt(orderItem.getDeliverAt());
        if (null == omsOrderItem.getPaymentAt()) {
            omsOrderItem.setPaymentAt(orderItem.getDeliverAt());
        }
        updateByUuid(omsOrderItem);

        Order order = orderDao.findByUuid(orderItem.getOrderUuid());
        if (null == order.getDeliveredCount()) {
            order.setDeliveredCount(0);
        }
        order.setDeliveredCount(order.getDeliveredCount() + 1);

        if (order.getDeliveredCount().intValue() == order.getQuantity().intValue()) {
            order.signal(EOrderAction.LOGISTICS_DELIVERED);
        }
        orderDao.updateByUuid(order, order.getUuid());

        //accountService.doUnSettle(omsOrderItem);
        accountTransitUtils.accountTransit(orderItem, "doUnSettle");
    }

    @Override
    public void doDelivered(String expressCode, String carrierCode) {
//        LogisticsWaybill entity = logisticsWaybillDao.selectOne();
//        if (entity == null || entity.getDelivered()) {
//            return;
//        }
    }
}
